package edu.asu.sapa.ground;

import java.util.ArrayList;
import java.util.Arrays;

import edu.asu.sapa.Planner;
import edu.asu.sapa.ground.update.Condition;
import edu.asu.sapa.ground.update.Context;
import edu.asu.sapa.ground.update.Load;
import edu.asu.sapa.ground.update.StartPointLoader;
import edu.asu.sapa.ground.update.Update;
import edu.asu.sapa.heuristic.CostFunction;

public class Operator extends CostFunction<ArrayList<Integer>> {
	public static final Operator nullOperator = new Operator(); 
	// id = -1, name = [null], name.length = 1
	// initOperator.id = 0, name = [], name.length = 0
	
	private String stringName;

	public MathForm duration;
	public MathForm cost;

	public Load[] contents;
	
	public boolean isPossible;
	
	private Operator() {
		super(new ArrayList<Integer>(1));
		name.add(0);
		stringName = "";
		id=-1;

		duration = MathForm.NaN;
		cost = MathForm.NaN;
		contents = new Load[1];
		contents[0] = new StartPointLoader();
		contents[0].contents = new Update[1];
		contents[0].contents[0] = Condition.bottom;
		
		isPossible = false;
	}
	
	public Operator(ArrayList<Integer> name) {
		super(name);

		stringName = null; // lazily set
		duration = MathForm.NaN;
		cost = MathForm.NaN;
		isPossible = false;
	}
	
	public Object clone() {
		Operator o = (Operator)super.clone();

		// shallow copies are fine -- if we want to overwrite effects we assign a new reference, not alter the actual effect
		o.contents = Arrays.copyOf(contents,contents.length);
		
		// very shallow copies sufficient
		// this is only used to clone whole groundings, presumably for scratch considerations by Monitor() (i.e., updates)
		// so having top level structures independent is fine; bottom levels can share --
		// we wouldn't be considering altering only part of a duration or cost formula.  Only the whole thing.
		// also, this is really only used for the initoperator anyways.
		
		// so shouldn't o.duration = duration be fine then? [ditto cost?]
		o.duration = new MathForm(duration);
		o.cost = new MathForm(cost);

		return o;
	}

	public Context makeContext(State s) {
		Context c = new Context(id);
		float start = s.time; 
		c.start = start;
		float dur = duration.value(s,c);
		c.duration = dur;
		c.finish = start + dur;
		c.cost = cost.value(s,c);
		return c;
	}
	public void makeContext(State s,Context c) {
		c.id = id;
		float start = s.time; 
		c.start = start;
		float dur = duration.value(s,c);
		c.duration = dur;
		c.finish = start + dur;
		c.cost = cost.value(s,c);
	}
	
	public boolean applicable(State s, Context c) {
		if (s.time != c.start)
			System.err.println("Whhhatt???");
		if (id <= s.operator || c.duration != c.duration)
			return false;
		boolean possible = true;
		for (Load l : contents) {
			possible &= l.applicableBefore(s,c);
		}
		return possible;
	}

	public boolean update(State s, Context c) {
		if (s.time != c.start)
			System.err.println("Whhhatt???");
		float cst = cost.value(s,c);
		if (cst != cst)
			return false;
		s.operator = id;
		s.c = c;
		boolean possible = true;
		for (Load l : contents) {
			possible &= l.load(s,c);
		}
		s.g += 1;
		s.gCost += cst;
		return possible;
	}
	
	public boolean analyzeStatic() {
		isPossible = true;

		float dur = Float.NaN;
		if (duration.analyzeStatic(dur)) {
			dur = duration.value;
			if (dur != dur)
				return isPossible = false;
		}
		else {
			// only for planning graph
			if(duration.value != duration.value)
				duration.value = 1;
		}

		if (cost.analyzeStatic(dur)) {
			if (cost.value != cost.value)
				return isPossible = false;
		} else {
			// only for planning graph
			if (cost.value < 1 || cost.value != cost.value) {
				cost.value = 1;
			}
		}

		int size = contents.length;
		int i=0;
		while (i<size) {
			Load l = contents[i];
			if (!l.analyzeStatic(dur)) {
				contents = null;
				return false;
			}
			if (l.isNoOp()) {
				contents[i] = contents[--size];
			} else {
				++i;
			}
		}
		if (size != contents.length)
			contents = Arrays.copyOf(contents,size);
		
		return isPossible;
	}

	void removeDependencies() {
		for (Load l : contents) {
			l.removeDependencies(this);
		}
	}
	void setDependencies() {
		for (Load l : contents) {
			l.setDependencies(this);
		}
//		for (ProtectTest t : protectTestList) {
//			// nothing to do
//		}
	}

	public float getCost(State s, Context c) {
		return cost.value(s, c);
	}

	public float getDuration(State s, Context c) {
		return duration.value(s, c);
	}

	public String getName() {
		if (stringName == null) {
			if (name != null && name.size() > 0 && name.get(0) != 0) {
				StringBuilder str = new StringBuilder(50);
				int id = name.get(0);
				str.append('(').append(Planner.liftedProblem.actions.get(id).name);
				for (int i = 1, size = name.size(); i < size; ++i) {
					id = name.get(i);
					str.append(' ').append(
							Planner.liftedProblem.constants.get(id).name);
				}
				str.setCharAt(0, '(');
				str.append(')');
				stringName = str.toString();
			} else {
				stringName = Planner.liftedProblem.actions.get(0).name;
			}
		}
		return stringName;
	}

	public void setCost(float f) {
		cost = new MathForm(f);
	}

	public void setCost(MathForm m) {
		cost = m;
	}

	public void setDuration(float f) {
		duration = new MathForm(f);
	}

	public void setDuration(MathForm m) {
		duration = m;
	}

	public String toString() {
		String s = new String();
		s += "(:durative-action " + getName() + "\n";
		s += ":duration (= ?duration ";
		// if( d_isConstant)
		// s += d_constant;
		// else
		s += duration;
		s += ")\n";
		s += ":cost ";
		// if( c_isConstant)
		// s += c_constant;
		// else
		s += cost;
		s += "\n";

		s += ":update (and\n";
		for (Load l : contents) {
			s += l;
		}
		s += "))\n";
		
/*
  		s += ":condition (and";
 
		for (Condition c : conditionList) {
			s += "\t" + c + "\n";
		}
		for (ProtectCondition c : protectConditionList) {
			s += "\t" + c + "\n";
		}
		for (Lock c : lockProps) {
			s += "\t" + c + "\n";
		}
		for (ProtectTest c : protectTestList) {
			s += "\t" + c + "\n";
		}
		s += "\t)\n";

		s += ":effect (and\n";
		for (Assign c : effectList) {
			s += "\t" + c + "\n";
		}
		for (Set c : setList) {
			s += "\t" + c + "\n";
		}
		s += "\t)\n";

		s += ")\n";
*/
		
		return s;
	}

	
	// for PG
	public int nConditionsSat;

	public final void deactivate() {
		size = 0;
		selectedTime = Float.NaN;
		//reachable = false;
		nConditionsSat = 0;
	}

	
	
}
